/*
* JaxNav Dynamic One-Page Ajax Navigation Library for jQuery
* http://code.google.com/p/jax-nav
*
* Version - 0.3 (November 21, 2010)
* 
* Copyright 2010, Avinoam Henig
* Licensed under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
* 
* Required Files:
* jQuery v1.4.2 (http://code.jquery.com/jquery-1.4.2.min.js)
* HashChange jQuery Special Event (http://github.com/cowboy/jquery-hashchange/raw/v1.3/jquery.ba-hashchange.min.js)
*/

(function($){
	
	$.fn.jaxNav = function(){
		
		// Save jaxNav function arguments for use in the $.each loop function.
		var args = arguments;
		
		$(this).each(function(){
			
			var $el = $(this);
			
			// Execute correct functions according to the first argument.
			// Create funcs array to store the correct function names in the correct order.
			var funcs = [];
			
			// If there is no first argument or the first argument is not a string then it is an initializing function.
			if(typeof args[0] !== 'string'){
				funcs.push('init');
				
				// If objects for pages and/or options were passed, add them to the funcs.
				if(typeof args[0] === 'object') funcs.push('add');
				if(typeof args[1] === 'object') funcs.push('options');
				
			} else if(typeof args[0] === 'string') funcs.push(args[0].toLowerCase()); // If argument zero is a string run the specified function.
									
			// Loop through func and execute the correct functions, passing the $el and args.
			for(var i=0; i<funcs.length; i++) jaxNavFuncs[funcs[i]]($el, args);
			
		});
	
		return this;
	};
	
	// Local object to store all the different jaxNav functions for easy access.
	var jaxNavFuncs = {
		
		// Initialize the element for Jax Nav behaviors by addding data to store pages and options etc... And bind a hashchange event.
		init : function($el){
			
			// Stop if the element has already been initialized.
			if($el.data('jaxNav')) return;
			
			// Create global variable _JaxNavREQUEST to store passed request values ('#pageName?name=val').
			_JaxNavREQUEST = new Array();
			
			// Attach data to the element to store the pages, options, keep track of the page, and store the hashchange event handler.
			$el.data('jaxNav', {	
				pages : {},
				options : {
					trans : false
				},
				
				// Variable used to pause page changes while a transition is being run.
				transitionPause : false,
				
				// Store the current page info, and is used to get the old page when switching pages. This variable is updated when the page
				// is finished switching.
				page : {
					name : false,
					html : false
				},
				
				// The hash that got the current page.
				curHash : '',
				
				// Hashchange event handler to bind to the hashchange event. We put it in a seperate function in the element data
				// so that we can unbind it in 'destroy'.
				hashchange : function(){
					// Don't run the function if a transition is being run. Hashchange will be called again on pagetransdone.
					if($el.data('jaxNav').transitionPause) return;
					
					// Parse the name and request values from the hash.
					var hash = window.location.hash,
						name = hash.match(/[^?#]+/) ? hash.match(/[^?#]+/)[0] : '',
						requestString = hash.match(/\?.+/) ? hash.match(/\?.+/)[0].replace(/\?/, '') : '',
						requestArray = requestString.split(/\&/g), 
						curVal;
					// If the page will change then clear the request array.
					if($el.data('jaxNav').pages[name]) _JaxNavREQUEST = new Array();
					// Parse requestArray into key=>value for the _JaxNavREQUEST array.
					for(var i = 0; i<requestArray.length; i++){
						curVal = requestArray[i].split('=');
						if(curVal.length===2) _JaxNavREQUEST[curVal[0]] = curVal[1];
					}
					
					// Check if there is a page with that name.
					if($el.data('jaxNav').pages[name]){
						
						$el.data('jaxNav').curHash = hash;
						
						// Make an ajax call to retrieve the page contents.
						$.post($el.data('jaxNav').pages[name], requestString, function(page){
							
							// Save the new page data to pass through the events.
							var newPage = {
								name: name,
								html: page
							};
							
							// Sava old page from the $el's data.
							var oldPage = $el.data('jaxNav').page;
							
							// Update the element data's page to the current page.
							$el.data('jaxNav').page = newPage;
							
							// If an transition function is specified, run it.
							if($el.data('jaxNav').options.trans){
								
								// Save the current element to $oldPage.
								var $oldPage = $el;
								
								// Clone old page with all its data and events, change the html to the new page, and insert it after $oldPage.
								// And make it $el.
								$el = $oldPage.clone(true).html(page).hide().insertAfter($oldPage);
								
								// Pause all other page changes while this transition is running.
								$el.data('jaxNav').transitionPause = true;
																
								// Run the transition function with passing the $oldPage, and the $newPage, and make the done function.
								$el.data('jaxNav').options.trans($oldPage, $el, function(){
								
									// Remove the old page from the DOM.
									$oldPage.remove();
									
									// Trigger the pagetransdone event with passing oldPage and page through the event object.
									$el.trigger({
										type : 'pagetransdone',
										oldPage : oldPage,
										page : newPage
									});
									
								});
								
							} else $el.html(page); // If no transition function was specified just change $el's html to the new page.
							
							// Trigger the pagechange event with passing oldPage and page through the event object.
							$el.trigger({
								type : 'pagechange',
								oldPage : oldPage,
								page : newPage
							});
							
						});
					};
				}
			});
			
			// Bind the hashchange function in the element's data to the hashchange event.
			$(window).bind('hashchange', $el.data('jaxNav').hashchange);
			
		},
		
		// A function to remove all JaxNav behaviors from $el.
		destroy : function($el){
			
			// Unbind $el's haschange function from the hashchange event.
			$(window).unbind('hashchange', $el.data('jaxNav').hashchange);
			
			// Remove $el's 'jaxNav' data.
			$el.removeData('jaxNav');
			
		},
		
		// A function to add pages.
		add : function($el, args){
			
			// If it's being called during init we need the first argument, if not we need the second.
			if(typeof args[0] === 'string') var index = 1;
			else var index = 0;
			
			// Make sure the argument is an object, and then add it to the pages object for $el.
			if(typeof args[index] === 'object'){
				$.extend($el.data('jaxNav').pages, args[index]);
				// Trigger the hashchange event in case the current hash matches one of the added pages.
				$(window).trigger('hashchange');
			};
			
		},
		
		// Function to remove pages.
		remove : function($el, args){
			
			// Loop through the arguments starting at the second one.
			for(var i=1; i<args.length; i++){
				// If the specified page exists delete it from the elements 'jaxNav' data.
				if($el.data('jaxNav').pages[args[i]]) delete $el.data('jaxNav').pages[args[i]];
			};
			
		},
		
		// Function to edit the jaxNav options.
		options : function($el, args){
			
			// Make sure the second argument is an object, and then add it to the options object for $el.
			if(typeof args[1] === 'object') $.extend($el.data('jaxNav').options, args[1]);
			
			// If the index was specified and there is no page being displayed currently then 'goto' index.
			if($el.data('jaxNav').options.index && !$el.data('jaxNav').pages[window.location.hash.match(/[^?#]+/) ? window.location.hash.match(/[^?#]+/)[0] : '']){
				$el.jaxNav('goto', $el.data('jaxNav').options.index);
			};
			
		},
		
		// Function to switch to any specified page.
		'goto' : function($el, args){
			
			// If the specified page exists then change the hash to that page name (which will automatically trigger the hashchange 
			// event which will change the page).
			if($el.data('jaxNav').pages[args[1].match(/[^?#]+/)]){
				window.location.hash = '#'+args[1];
			};
			
		}
		
	};
	
	// Create a jQuery special event 'pagechange' which will be triggered every time the page is changed.
	$.event.special.pagechange;
	
	// Create shortcut function for the pagechange event.
	$.fn.pagechange = function(func){
		return func ? $(this).bind('pagechange', func) : $(this).trigger('pagechange');
	};
	
	// Create a jQuery special event 'pagetransdone' which will be triggered when a page animation is finished.
	$.event.special.pagetransdone = {
		
		_default : function(event){
			
			// Set transitionPause back to false.
			$(event.target).data('jaxNav').transitionPause = false;
			
			// Trigger hashchange to switch to the next page if one was selected during animation.
			if($(event.target).data('jaxNav').curHash !== window.location.hash) $(window).trigger('hashchange');
			
		}
		
	};
	
	// Create shortcut function for the pagetransdone event.
	$.fn.pagetransdone = function(func){
		return func ? $(this).bind('pagetransdone', func) : $(this).trigger('pagetransdone');
	};
	
})(jQuery);